import { Message } from 'element-ui'

// 最大长度
const maxLength = (e, max) => {
    if (e.value.length > max) {
        e.value = e.value.substr(0, max)
    }
}

// 最大小数位数
const floatLength = (e, max) => {
    const n = e.value.match(/\.\d*/)?.[0]
    if (n && n.length + 1 > max) {
        e.value = e.value.replace(/\.\d*/, n.substr(0, max + 1)).replace(/\.$/, '')
    }
}

// 大小范围
const scopeSize = (e, { min, max }) => {
    if (e.value.length) {
        if (e.value < min) {
            e.value = min.toString()
        } else if (e.value > max) {
            e.value = max.toString()
        }
    }
}

// 小数补零
const floatPadZero = (e, max) => {
    let v = e.value
    if (!v.length) return
    if (!~v.indexOf('.')) {
        v += '.'
    }
    if (v[0] === '.') {
        v = '0' + v
    }
    const l = v.length - v.indexOf('.') - 1
    if (l < max) {
        e.value = v.padEnd(max - l + v.length, '0')
    }
}

// 去除前面多余0，小数点前补0
const beforeZero = e => e.value = e.value.replace(/^0+(\d)/, '$1').replace(/^\./, '0.')

// -0转0
const minusZero = e => {
    if (+e.value !== 0) return
    e.value = e.value.replace(/^-+/, '')
}

// 次方转换为数字（1e2 => 100）
const powerToNumber = e => {
    const v = e.value
    if (!v.length) return
    if (v.includes('e')) {
        e.value = (+v).toString()
    }
}

// 去除所有空格
const deleteSpace = e => e.value = e.value.replace(/\s/g, '')

// 大小范围-超出通知
const scopeSizeOverTips = (el, dataDiffe, getNewValue) => dataDiffe(
    e => scopeSize(e, getNewValue()),
    (newV, oldV, type) => {
        const { tipsMin, tipsMax } = el.$binding.value
        if (oldV > +newV) {
            if (!tipsMax) return
            typeof tipsMax === 'function' ? tipsMax(newV, oldV, type) : Message.warning('不能大于' + newV)
        } else if (oldV < +newV) {
            if (!tipsMin) return
            typeof tipsMin === 'function' ? tipsMin(newV, oldV, type) : Message.warning('不能小于' + newV)
        }
    }
)

// 是否已经绑定过事件
const isBinding = (el, binding, inputDom) => {
    binding.value ||= {}
    if (Object.hasOwn(binding, 'oldValue')) {
        if (JSON.stringify(binding.oldValue) !== JSON.stringify(binding.value) || binding.oldArg !== binding.arg) {
            // 更新数据
            el.$binding = binding
            inputDom.dispatchEvent(new Event('blur'))
        }
        return true
    }
    el.$binding = binding
}

// 过滤拼写输入
const filterSpell = dom => {
    dom.addEventListener('compositionstart', e => e.target.composing = true)
    dom.addEventListener('compositionend', e => {
        e.target.composing = false
        e.target.dispatchEvent(new Event('input', { bubbles: true }))
    })
    dom.addEventListener('input', e => {
        e.target.composing && e.stopImmediatePropagation()
    }, false)
}

// 输入框验证：el-input
export const input_valid = (el, binding, vnode) => {
    const inputDom = el.querySelector('input,textarea') || el
    if (isBinding(el, binding, inputDom)) return
    // 非法输入重置输入框
    inputDom.addEventListener('blur', e => e.target.value = e.target.value)
    // 更新数据
    const update = val => {
        if (el.$binding.value.beforeChange?.(val, inputDom.value) === false) return
        inputDom.value = val
        inputDom.dispatchEvent(new Event('input', { bubbles: true }))
    }
    // 数据比较
    const dataDiffe = (cb, done = update) => e => {
        const oldV = e.target ? e.target.value : e.value
        const newVs = { value: oldV }
        cb(newVs)
        newVs.value !== oldV && done(newVs.value, oldV, e.type)
    }
    // 空值回显：v-input-valid:[默认值].xxx=""
    {
        const eventCb = e => {
            if (!e.isTrusted) return
            if (vnode.componentInstance.value?.length || typeof vnode.componentInstance.value === 'number') return
            if (el.$binding.arg || el.$binding.arg === 0) {
                update(el.$binding.arg.toString())
            }
        }
        inputDom.addEventListener('blur', eventCb)
        eventCb({ isTrusted: true })
        vnode.componentInstance.$on('clear', vnode.componentInstance.focus)
    }
    // 过滤拼写输入：v-input-valid.filterSpell.xxx=""
    if (binding.modifiers.filterSpell) {
        filterSpell(inputDom)
    }
    // 全部模式
    {
        let inputEvent
        let blurEvent
        let tipsCb
        // 获取最新 binding.value
        const getNewValue = _ => Object.entries(getNewValue.value).reduce((p, [k, v]) => {
            p[k] = el.$binding.value.hasOwnProperty(k) ? el.$binding.value[k] ?? v : v
            return p
        }, {})
        /* 整数：v-input-valid.int="{
                beforeChange: 改变前回调<function> = (newV, oldV) => {},
                tipsMin: 超出最小值提示<boolean|function> = (newV, oldV, type) => {},
                tipsMax: 超出最大值提示<boolean|function> = (newV, oldV, type) => {},
                min: 最小值<number> = 1,
                max: 最大值<number> = 999999999,
            }"
            type="number"
        */
        if (binding.modifiers.int) {
            getNewValue.value = {
                min: 1,
                max: 999999999,
            }
            inputEvent = e => {
                floatLength(e, 0)
            }
            blurEvent = e => {
                scopeSize(e, getNewValue())
                minusZero(e)
                beforeZero(e)
                powerToNumber(e)
            }
            tipsCb = scopeSizeOverTips(el, dataDiffe, getNewValue)
        }
        /* 小数：v-input-valid.float="{
                beforeChange: 改变前回调<function> = (newV, oldV) => {},
                tipsMin: 超出最小值提示<boolean|function> = (newV, oldV, type) => {},
                tipsMax: 超出最大值提示<boolean|function> = (newV, oldV, type) => {},
                min: 最小值<number> = 0.01,
                max: 最大值<number> = 999999999,
                float: 小数位数<number> = 2,
            }"
            type="number"
        */
        // 是否补零：v-input-valid.float.pad
        else if (binding.modifiers.float) {
            getNewValue.value = {
                min: 0.01,
                max: 999999999,
                float: 2,
            }
            inputEvent = e => {
                floatLength(e, getNewValue().float)
            }
            blurEvent = e => {
                scopeSize(e, getNewValue())
                minusZero(e)
                beforeZero(e)
                powerToNumber(e)
                el.$binding.modifiers.pad && floatPadZero(e, getNewValue().float)
            }
            tipsCb = scopeSizeOverTips(el, dataDiffe, getNewValue)
        }
        /* 数字：v-input-valid.number="{
                beforeChange: 改变前回调<function> = (newV, oldV) => {},
                maxLength: 最大长度<number> = 11,
            }"
            type="number"
        */
        else if (binding.modifiers.number) {
            getNewValue.value = {
                maxLength: 11,
            }
            inputEvent = e => {
                floatLength(e, 0)
                maxLength(e, getNewValue().maxLength) // type="number"时，maxlength无效
            }
        }
        /* 大写字母和数字：v-input-valid.upperNum="{
                beforeChange: 改变前回调<function> = (newV, oldV) => {},
            }"
            :maxlength=""
        */
        else if (binding.modifiers.upperNum) {
            inputDom.style.textTransform = 'uppercase'
            blurEvent = e => {
                e.value = e.value.toUpperCase().replace(/\W|_/g, '') // 只允许输入大写字母和数字
            }
        }
        // 禁止输入空格：v-input-valid.space
        else if (binding.modifiers.space) {
            inputEvent = e => {
                deleteSpace(e)
            }
        }
        /* 自定义正则：v-input-valid="{
                beforeChange: 改变前回调<function> = (newV, oldV) => {},
                oninput: 正则表达式<RegExp>,
                onblur: 正则表达式<RegExp>,
            }"
        */
        else {
            const { inputReg, blurReg } = binding.value
            if (inputReg) {
                inputEvent = e => {
                    e.value = e.value.replace(inputReg, '')
                }
            }
            if (blurReg) {
                blurEvent = e => {
                    e.value = e.value.replace(blurReg, '')
                }
            }
        }
        // 异常输入提示
        if (tipsCb) {
            inputDom.addEventListener('input', tipsCb)
            inputDom.addEventListener('blur', tipsCb)
        }
        // 注册事件
        inputEvent && inputDom.addEventListener('input', dataDiffe(inputEvent))
        blurEvent && inputDom.addEventListener('blur', dataDiffe(blurEvent))
    }
}
